#include "drv_flash.h"
union u32_u8{
	uint32_t word;
	uint8_t ch[4];
};
union u32_u8 Data;

uint32_t F460FLASH_ReadWord(uint32_t faddr){
	return *(vu32*)faddr; 
}

void EFM_Program(uint32_t WriteAddr,uint16_t NumToWrite,uint8_t *pBuffer){
	uint16_t i=0;
	uint32_t temp=0;
	for(i=0;i<NumToWrite/4;i++){
		Data.ch[0]=pBuffer[i*4];
		Data.ch[1]=pBuffer[i*4+1];
		Data.ch[2]=pBuffer[i*4+2];
		Data.ch[3]=pBuffer[i*4+3];
		EFM_SingleProgram(WriteAddr,Data.word);
		WriteAddr+=4;
	}
	if(NumToWrite%4==1){
		temp=pBuffer[i*4]|0xFFFFFF00;
		EFM_SingleProgram(WriteAddr,temp);
	}else if(NumToWrite%4==2){
		temp=pBuffer[i*4]|(uint32_t)(pBuffer[i*4+1])<<8|0xFFFF0000;
		EFM_SingleProgram(WriteAddr,temp);
	}else if(NumToWrite%4==3){
		temp=pBuffer[i*4]|(uint32_t)(pBuffer[i*4+1])<<8|(uint32_t)(pBuffer[i*4+2])<<16|0xFF000000;
		EFM_SingleProgram(WriteAddr,temp);
	}
}

//从指定地址开始写入指定长度的数据
//WriteAddr:起始地址(此地址必须为8的倍数!!)
//pBuffer:数据指针
//NumToWrite:双字(64位)数(就是要写入的64位数据的个数.) 
uint8_t F460FLASH_BUF[F460_SECTOR_SIZE];//最多是8K字节
void F460FLASH_Write(uint32_t WriteAddr,uint8_t *pBuffer,uint16_t NumToWrite){
	uint32_t sectorpos;	   //页地址
	uint16_t sectoroff;	   //页内偏移地址(64位双字计算)
	uint16_t sectorremain; //页区内剩余地址(64位双字计算)	   
 	uint16_t i;    
	uint32_t offaddr;   //去掉0X08000000后的地址
	if(WriteAddr>=(F460_FLASH_BASE+1024*F460_FLASH_SIZE)){
		return;//非法地址
	}
	EFM_Unlock();			//解锁
	EFM_FlashCmd(Enable);
    /* Wait flash ready. */
	while(Set != EFM_GetFlagStatus(EFM_FLAG_RDY)){;}
	offaddr=WriteAddr-F460_FLASH_BASE;		//实际偏移地址.
	sectorpos=offaddr/F460_SECTOR_SIZE;			//扇区地址  
	sectoroff=(offaddr%F460_SECTOR_SIZE)/4;		//扇区内的偏移(4个字节为基本单位.)
	sectorremain=F460_SECTOR_SIZE/4-sectoroff;//扇区剩余空间大小   
	if(NumToWrite<=sectorremain){
		sectorremain=NumToWrite;//不大于该扇区范围
	}
	while(1) {	
		F460FLASH_Read(sectorpos*F460_SECTOR_SIZE+F460_FLASH_BASE,F460FLASH_BUF,F460_SECTOR_SIZE);//读出整个页的内容
		for(i=0;i<sectorremain*4;i++){	//校验数据
			if(F460FLASH_BUF[sectoroff*4+i]!=0xFF){
				break;//需要擦除
			}				
		}
		if(i<sectorremain){				//需要擦除
			EFM_SectorErase(sectorpos*F460_SECTOR_SIZE);
			for(i=0;i<sectorremain*4;i++){//复制
				F460FLASH_BUF[i+sectoroff*4]=pBuffer[i];	  
			}
			EFM_Program(sectorpos*F460_SECTOR_SIZE, F460_SECTOR_SIZE, F460FLASH_BUF);
		}else {
			EFM_Program(WriteAddr,sectorremain*4, pBuffer);//写已经擦除了的,直接写入页剩余区间. 
		}
		if(NumToWrite==sectorremain){
			break;//写入结束了
		}else{//写入未结束
			sectorpos++;				//扇区地址增1
			sectoroff=0;				//偏移位置为0 	 
			pBuffer+=sectorremain;  	//指针偏移
			WriteAddr+=sectorremain*4;	//写地址偏移(32位数据地址,需要*4)	   
			NumToWrite-=sectorremain;	//字节(32位)数递减
			if(NumToWrite>(F460_SECTOR_SIZE/4)){
				sectorremain=F460_SECTOR_SIZE/4;//下一个扇区还是写不完
			}else{
				sectorremain=NumToWrite;//下一个扇区可以写完了
			}
		}	
	};	
	EFM_Lock();	//上锁
}

void F460FLASH_Sector_Erase(uint32_t WriteAddr,uint32_t num){	
	uint32_t cnt=0;
	if(WriteAddr>=(F460_FLASH_BASE+1024*F460_FLASH_SIZE)){
		return;//非法地址
	}
	EFM_Unlock();			//解锁
	EFM_FlashCmd(Enable);
    /* Wait flash ready. */
	while(Set != EFM_GetFlagStatus(EFM_FLAG_RDY)){;}
	while(1) {	
		EFM_SectorErase(WriteAddr);
		WriteAddr+=8192;
		cnt+=8192;
		if(cnt>num){
			break;
		}
	}	
	EFM_Lock();	//上锁
}

void F460FLASH_Write1(uint32_t WriteAddr,uint8_t *pBuffer,uint16_t NumToWrite){
	uint32_t sectorpos;	   //页地址
	uint16_t sectoroff;	   //页内偏移地址(64位双字计算)
	uint16_t sectorremain; //页区内剩余地址(64位双字计算)	   
 	uint16_t i;    
	uint32_t offaddr;   //去掉0X08000000后的地址
	if(WriteAddr>=(F460_FLASH_BASE+1024*F460_FLASH_SIZE)){
		return;//非法地址
	}
	EFM_Unlock();			//解锁
	EFM_FlashCmd(Enable);
    /* Wait flash ready. */
	while(Set != EFM_GetFlagStatus(EFM_FLAG_RDY)){;}	
	EFM_Program(WriteAddr,NumToWrite, pBuffer);//写已经擦除了的,直接写入页剩余区间. 	
	EFM_Lock();	//上锁
}

//从指定地址开始读出指定长度的数据
//ReadAddr:起始地址
//pBuffer:数据指针
//NumToRead:字(32位)数
void F460FLASH_Read(uint32_t ReadAddr,uint8_t *pBuffer,uint16_t NumToRead){
	uint16_t i;
	uint32_t temp;
	for(i=0;i<NumToRead/4;i++){
		Data.word=F460FLASH_ReadWord(ReadAddr);//读取4个字节.
		ReadAddr+=4;//偏移4个字节.	Data
		pBuffer[i*4]=Data.ch[0];
		pBuffer[i*4+1]=Data.ch[1];
		pBuffer[i*4+2]=Data.ch[2];
		pBuffer[i*4+3]=Data.ch[3];
	}
}
